package com.abpa.util.unidata.impl;

import asjava.uniobjects.UniCommand;
import asjava.uniobjects.UniCommandException;
import asjava.uniobjects.UniDictionary;
import asjava.uniobjects.UniFile;
import asjava.uniobjects.UniFileException;
import asjava.uniobjects.UniSessionException;
import com.abpa.util.unidata.*;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author msowders
 */
public class CommandImpl implements Command {
    private Map<String, String> fieldsToWrite = new HashMap<String,String>();
    private Command.Type type;
    private String command = "";
    private String response;
    private String filename;
    private String id;

    CommandImpl(){
        this.type = Type.LIST;
    }

    private void executeDelete(Session session) throws DataAccessException {
        try {
            UniFile file = session.getSession().open(this.filename);
            file.deleteRecord(id);
        } catch (UniFileException ex) {
            Logger.getLogger(CommandImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new DataAccessException("Exception thrown deleting on file: " + this.filename + ":" + this.id, ex);
        } catch (UniSessionException ex) {
            Logger.getLogger(CommandImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new DataAccessException("Exception thrown accessing file: " + this.filename + ":" + this.id, ex);
        }
    }
    private void executeDeleteDict(Session session) throws DataAccessException {
        try {
            UniDictionary file = session.getSession().openDict(this.filename);
            file.deleteRecord(id);
        } catch (UniFileException ex) {
            Logger.getLogger(CommandImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new DataAccessException("Exception thrown deleting on dictionary of: " + this.filename + ":" + this.id, ex);
        } catch (UniSessionException ex) {
            Logger.getLogger(CommandImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new DataAccessException("Exception thrown accessing dictionary for file: " + this.filename + ":" + this.id, ex);
        }
    }

    private void executeWrite(Session session) throws DataAccessException {
        try {
            UniFile file = session.getSession().open(this.filename);
            file.setRecordID(this.id);
            for(Map.Entry<String,String> entry : this.fieldsToWrite.entrySet()){
                file.writeNamedField(entry.getKey(), entry.getValue());
            }
        } catch (UniFileException ex) {
            Logger.getLogger(CommandImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new DataAccessException("Exception thrown deleting on file: " + this.filename + ":" + this.id, ex);
        } catch (UniSessionException ex) {
            Logger.getLogger(CommandImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new DataAccessException("Exception thrown accessing file: " + this.filename + ":" + this.id, ex);
        }
    }
    private void executeWriteDict(Session session) throws DataAccessException {
        try {
            UniDictionary file = session.getSession().openDict(this.filename);
            file.setRecordID(this.id);
            for(Map.Entry<String,String> entry : this.fieldsToWrite.entrySet()){
                file.writeNamedField(entry.getKey(), entry.getValue());
            }
        } catch (UniFileException ex) {
            Logger.getLogger(CommandImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new DataAccessException("Exception thrown deleting on file: " + this.filename + ":" + this.id, ex);
        } catch (UniSessionException ex) {
            Logger.getLogger(CommandImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new DataAccessException("Exception thrown accessing file: " + this.filename + ":" + this.id, ex);
        }
    }

    private void executeRead(Session session) throws DataAccessException {
        //TODO need to implement caching of command responses
        try {
            UniCommand uCommand = session.getSession().command(this.getCommand());
            Logger.getLogger(UniSessionWrapper.class.getName()).log(Level.INFO, "executing", this.getCommand());
            uCommand.exec();
            this.setResponse(uCommand.response());
            Logger.getLogger(CommandImpl.class.getName()).log(Level.INFO, this.getResponse(), this.getCommand());
        } catch (UniCommandException ex) {
            Logger.getLogger(CommandImpl.class.getName()).log(Level.SEVERE, ex.getErrorTypeText() + " " + ex.getExtendedMessage(), ex);
            throw new DataAccessException("Command exception thrown executing on " + session.getAccount() + ":" + this.getCommand(), ex);
        } catch (UniSessionException ex) {
            Logger.getLogger(CommandImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new DataAccessException("Session exception thrown executing on " + session.getAccount() + ":" + this.getCommand(), ex);
        }
    }

    public void execute(Session session) throws DataAccessException {
        switch(type){
            case LIST:
            case LISTDICT:
                executeRead(session); break;
            case WRITE:
                executeWrite(session); break;
            case WRITEDICT:
                executeWriteDict(session); break;
            case DELETE:
                executeDelete(session); break;
            case DELETEDICT:
                executeDeleteDict(session); break;
        }
    }

    public String getCommand() {
        return this.command;
    }

    public String getResponse() {
        return this.response;
    }

    public void setCommand(String command) {
        this.command = command;
    }

    private void setResponse(String response) {
        this.response = response;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public void setFile(String filename) {
        this.filename = filename;
    }

    public void write(String fieldname, String data) {
        this.fieldsToWrite.put(fieldname, data);
    }
}
